/*
* Copyright 2004-2010 mb_sx@hotmail.com
* All Rights Reserved.
*/
using System;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Data;
using System.Collections;
using System.Drawing;
using System.Resources;
using System.IO;
using System.Security;
using System.Reflection;
using System.Xml;
using System.Threading;
using System.Diagnostics;
using System.Drawing.Printing;
using Outlook = Microsoft.Office.Interop.Outlook;
using System.Collections.Generic;
using System.Timers;


namespace DeskNotes
{
	
	//public delegate void Saver();

	/// <summary>
	/// DeskNotes provides an access to underlying DataSet DeskNote and to XML file which
	/// stores actual data.
	/// It performs reading and initiating of notes collection on startup.
	/// Adding/updating/removing of notes during application usage through simplified interface.
	/// </summary>
	public class DeskNotesData : DeskNoteDB
	{
        public enum UsageType { FirstTime, OldVersionFound, Regular };

		public static string DEBUG_REGISTRY ="Software\\M.B.\\DeskNotes\\Debug\\Main";
		const string IMEASURE_KEY = "Control Panel\\International";
		const string IMEASURE_VALUE = "iMeasure";
		const double INCH_MM = .254;
        internal const string DEFAULT_DATA = "DeskNotes.xml";
        internal const string DEFAULT_ARCHIVE = "DeskNotes_Archive.xml";
        const int SAVE_TIMER_BASIC = 600000; // 10 minutes
        const int SAVE_TIMER_EXT = 1800000; // 30 minutes to check for change
        
        // List of Notes application handles.
        private List<DeskNotes.Note> notes = new List<Note>();

		private ArchivedNotes archive = null;
		private RemindersForm remindersForm = null;

        private NoteForm lastActiveForm;
        private bool groupFormsActivation = false;

        // Initial values against which new values when changed are compared and change is applied where needed.
        internal Color oldBackColor;
		private Color oldTextColor;
        private Font oldFont;
        internal bool oldDataFiles = false;
		
		private bool showEmptyNote = false;
        private System.Drawing.Printing.PageSettings pageSettings = new System.Drawing.Printing.PageSettings();

       
		internal ArchiveForm archiveForm = null;
        internal bool archiveModified = false;
		private static bool debug = false;
		private System.Windows.Forms.ToolStripMenuItem[] disabledPrintMenus;
		private NotePrintDocument notePrintDoc;
		private bool showingAllNotes = true;
        private bool indexReady = false;
        private System.Timers.Timer saveTimer;

        private bool outlookOK = false;

        internal bool OutlookOK
        {
            get { return outlookOK; }
            set { outlookOK = value; }
        }

        /// <summary>
        /// Stores the last active DeskNote form on windows, with focus.
        /// Used to reactivate this form when clicking on sys tray icon.
        /// </summary>
        internal NoteForm LastActiveForm
        {
            get { return lastActiveForm; }
            set { lastActiveForm = value; }
        }
        /// <summary>
        /// True if all visible forms are being activated by click on systray icon.
        /// Otherwise false.
        /// </summary>
        internal bool GroupFormsActivation
        {
            get { return groupFormsActivation; }
            set { groupFormsActivation = value; }
        }

		public static string Debug
		{
			set 
			{
				if (("debug" == value) || ("Debug" == value) || ("DEBUG" == value))
					debug = true;
			}
		}
        
		internal bool ShowingAllNotes
		{
			get 
			{
				return showingAllNotes;
			}
			set 
			{
				showingAllNotes = value;
			}
		}

	
		public ArchivedNotes Archive
		{
			get 
			{
				return archive;
			}
		}

        public bool PlaySound 
		{
			get 
			{
				return "" != Properties.Settings.Default.SoundFile ? true : false;
			}
		}

		
		/// <summary>
		/// Property tells the application if it is first start on users desktop.
        /// or older version DeskNotes data files are available or it is regular consequent start.
		/// </summary>
		public UsageType UsageStyle
		{
			get
			{
                if (oldDataFiles)
                    return UsageType.OldVersionFound;
				else if (Properties.Settings.Default.DataFile == "") 
					return UsageType.FirstTime;
				else
					return UsageType.Regular;
			}
		}

        internal System.Drawing.Printing.PageSettings PageSettings
        {
            set
            {
                pageSettings = value;
                Properties.Settings.Default.PrintMargins = value.Margins;
            }
            get
            {
                return pageSettings;
            }
        }

        

        /// <summary>
        /// Use when print margins are displayed to user. They are calculated to user measurement system.
        /// </summary>
		internal System.Drawing.Printing.PageSettings DisplayedPageSettings 
		{
			get
			{

                if (IsInchesMeasurement())
                {
                    return pageSettings;
                }
                else
                { //metrics system
                    // PageSetup dialog EnableMetrics property should convert to milimetrs automatically
                    // but it does not seem to have an effect. Therefore here is the workaround to get good mm

                    PageSettings pS = new PageSettings();
                    pS = (PageSettings)pageSettings.Clone();
                    Margins m = pS.Margins;

                    pS.Margins.Top = (int)Decimal.Round((decimal)(m.Top * INCH_MM),0)*10;
                    pS.Margins.Left = (int)Decimal.Round((decimal)(m.Left * INCH_MM),0)*10;
                    pS.Margins.Right = (int)Decimal.Round((decimal)(m.Right * INCH_MM),0)*10;
                    pS.Margins.Bottom = (int)Decimal.Round((decimal)(m.Bottom * INCH_MM),0)*10;
                    return pS;
                }
			}
		}

		/// <summary>
		/// Get info about measurement system of the current user.
		/// </summary>
		/// <returns>true if measurement is inches (US), false if metrics</returns>
		internal bool IsInchesMeasurement()
		{
			short measureInches = 1;
			RegistryKey key = Registry.CurrentUser;
			try
			{
				key = key.OpenSubKey(IMEASURE_KEY);
				measureInches = Int16.Parse((string)key.GetValue(IMEASURE_VALUE));
			} 
			catch (Exception e)
			{
				Logger.WriteLine("Exception reading iMeasure info: " + e.Message);
			}
			finally 
			{
				if (null != key)
					key.Close();
			}
			if (1 == measureInches)
				return true;
			else
				return false;
		}

		/// <summary>
		/// Reads settings written by 1.0 version of DeskNotes
		/// </summary>
		/// <returns>true - settings were read, otherwise false</returns>
		internal bool Read1_0Settings()
		{
			RegistryKey mainKey = Registry.CurrentUser;
			try 
			{
				mainKey = mainKey.OpenSubKey(MainResource.GetString("REGISTRY_KEY_1_0"));
				// if key does not exist then application is used for first time.
				if (mainKey == null)
				{
					return false;
				}
				else
				{
                    Properties.Settings settings = Properties.Settings.Default;
					string cult = (string) mainKey.GetValue("Culture");
					if (null != cult)
					{
						settings.Culture = cult;
					}
					settings.DataFile = (string) mainKey.GetValue("DataFile");
					
					object color =  mainKey.GetValue("BackgroundColor");
                    if (null != color)
                    {
                        settings.BackgroundColor = Color.FromArgb(int.Parse(color.ToString()));
                        this.oldBackColor = settings.BackgroundColor;
                    }
					color = mainKey.GetValue("TextColor");
                    if (null != color)
                    {
                        settings.TextColor = Color.FromArgb(int.Parse(color.ToString()));
                        this.oldTextColor = settings.TextColor;
                    }
					string sound = mainKey.GetValue("SoundFile").ToString();
					if ((null != sound ) && ("" != sound))
						settings.SoundFile = sound;
					string op = (string)mainKey.GetValue("Opacity");
					if (null != color)
						settings.Opacity = double.Parse(op.ToString());

					string fontName = (string)mainKey.GetValue("Font.Name");
					if (null != fontName)
					{
						float size = Single.Parse((string)mainKey.GetValue("Font.Size",10));

						FontStyle fontStyle = FontStyle.Regular;
						bool bold = Boolean.Parse((string)mainKey.GetValue("Font.Bold","false"));
						bool italic = Boolean.Parse((string)mainKey.GetValue("Font.Italic","false"));
						bool strikeout = Boolean.Parse((string)mainKey.GetValue("Font.Strikeout","false"));
						bool underline = Boolean.Parse((string)mainKey.GetValue("Font.Underline","false"));
						fontStyle |= bold ? FontStyle.Bold : 0;
						fontStyle |= italic ? FontStyle.Italic : 0;
						fontStyle |= strikeout ? FontStyle.Strikeout : 0;
						fontStyle |= underline ? FontStyle.Underline : 0;
						settings.Font = new Font(fontName, size,fontStyle);
                        this.oldFont = settings.Font;
					}
					return true;
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(MainResource.GetString("ERROR_READ_SETTINGS") + e.Message, 
					MainResource.GetString("APP_TITLE"),
					MessageBoxButtons.OK,MessageBoxIcon.Error);
				return false;
			} 
			finally 
			{
				if (null != mainKey)
					mainKey.Close();
			}
		}

        /// <summary>
        /// Reads settings from registry for version 1.5.
        /// </summary>
        /// <returns></returns>
        internal bool Read_1_5_Settings()
        {
            RegistryKey mainKey = Registry.CurrentUser;
            try
            {
                if (DeskNotesData.debug)
                    mainKey = mainKey.OpenSubKey(DeskNotesData.DEBUG_REGISTRY);
                else
                    mainKey = mainKey.OpenSubKey(MainResource.GetString("REGISTRY_KEY"));

                // if key does not exist then application is used for first time.
                if (mainKey == null)
                {
                    return Read1_0Settings();
                }
                else
                {
                    Properties.Settings settings = Properties.Settings.Default;
                    string cult = (string)mainKey.GetValue("Culture");
                    if (null != cult)
                    {
                        settings.Culture = cult;
                    }
                    settings.DataFile = (string)mainKey.GetValue("DataFile");
                    //archive can be null if this Reg value is not defined.
                    settings.ArchiveFile = (string)mainKey.GetValue("ArchiveFile");

                    object color = mainKey.GetValue("BackgroundColor");
                    if (null != color)
                    {
                        settings.BackgroundColor = Color.FromArgb(int.Parse(color.ToString()));
                        this.oldBackColor = settings.BackgroundColor;
                    }
                    color = mainKey.GetValue("TextColor");
                    if (null != color)
                    {
                        settings.TextColor = Color.FromArgb(int.Parse(color.ToString()));
                        this.oldTextColor = settings.TextColor;
                    }
                    string sound = (string) mainKey.GetValue("SoundFile");
                    if ((null != sound) && ("" != sound))
                        settings.SoundFile = sound;
                    string op = (string)mainKey.GetValue("Opacity");
                    if (null != op)
                        settings.Opacity = double.Parse(op.ToString());

                    object id = mainKey.GetValue("SelectedNodeId");
                    if (null != id)
                        settings.SelectedNodeId = long.Parse(id.ToString());
                    object type = mainKey.GetValue("SelectedNodeType");
                    if (null != type)
                        settings.SelectedNodeType = byte.Parse(type.ToString());

                    settings.InsertOnlyDate = Boolean.Parse((string)mainKey.GetValue("InsertOnlyDate", "false"));
                    settings.DragDropToArchive = Boolean.Parse((string)mainKey.GetValue("DragDropToArchive", "false"));

                    string fontName = (string)mainKey.GetValue("Font.Name");
                    if (null != fontName)
                    {
                        float size = Single.Parse((string)mainKey.GetValue("Font.Size", 10));

                        FontStyle fontStyle = FontStyle.Regular;
                        bool bold = Boolean.Parse((string)mainKey.GetValue("Font.Bold", "false"));
                        bool italic = Boolean.Parse((string)mainKey.GetValue("Font.Italic", "false"));
                        bool strikeout = Boolean.Parse((string)mainKey.GetValue("Font.Strikeout", "false"));
                        bool underline = Boolean.Parse((string)mainKey.GetValue("Font.Underline", "false"));
                        fontStyle |= bold ? FontStyle.Bold : 0;
                        fontStyle |= italic ? FontStyle.Italic : 0;
                        fontStyle |= strikeout ? FontStyle.Strikeout : 0;
                        fontStyle |= underline ? FontStyle.Underline : 0;
                        settings.Font = new Font(fontName, size, fontStyle);
                        this.oldFont = settings.Font;
                    }
                    return true;
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(MainResource.GetString("ERROR_READ_SETTINGS") + e.Message,
                    MainResource.GetString("APP_TITLE"),
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            finally
            {
                if (null != mainKey)
                    mainKey.Close();
            }
        }
	   

        /// <summary>
        /// When some of the properties has changed event is raised.
        /// Only few properties needs to be specially handled.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        internal void settings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            switch( e.PropertyName ){
                case null: //all properties has changed
                    break;
                case "BackgroundColor":
                    SetNotesBackgroundColor();
                    oldBackColor = Properties.Settings.Default.BackgroundColor;
                    break;
                case "TextColor":
                    //SetNotesTextColor();
                    oldTextColor = Properties.Settings.Default.TextColor;
                    break;
                case "DataFile":
                    // XML file is checked by reading into temporary dataset only if file exists
                    if (File.Exists(Properties.Settings.Default.DataFile)){
                        DeskNoteDB data = new DeskNoteDB();
                        data.ReadXml(Properties.Settings.Default.DataFile, XmlReadMode.Auto);
                    }
                   break;
                default:
                    break;
            }
            
        }

  		/// <summary>
		/// Method opens dataset for specified XML file. It handles all the errors which can happen 
		/// when reading the file on a disk.
		/// Method can throw an exception while reading the file.
		/// </summary>
		/// <returns>true if file was really open, otherwise false.</returns>
		internal bool OpenDataSet()
		{
			//perform data file checks
            if (File.Exists(Properties.Settings.Default.DataFile))
            {
                //dsDeskNotes = new DeskNote();
                this.ReadXml(Properties.Settings.Default.DataFile); //was dsDeskNotes.
                return true;
            }
            else //dataset filename was specified but file does not exist
            {
                string text = string.Format(MainResource.GetString("DATA_FILE_DOES_NOT_EXIST"), Properties.Settings.Default.DataFile);
                if (DialogResult.Yes == MessageBox.Show( text, MainResource.GetString("APP_TITLE"),
                    MessageBoxButtons.YesNo, MessageBoxIcon.Question))
                {
                    //dsDeskNotes = new DeskNote();
                    this.WriteXml(Properties.Settings.Default.DataFile, XmlWriteMode.WriteSchema); //was dsDeskNotes.
                    showEmptyNote = true;
                    return true;
                }
            }
            return false;
		}

		/// <summary>
		/// Method creates new DataSet in specified filename or tests if file is valid before open
		/// </summary>
		/// <param name="filename">file to be used for DeskNotes file creation</param>
		/// <returns>true if file name was created, otherwise false</returns>
		public bool CreateNewDataSet(string fileName)
		{
            // Creates dataset object instance
            DeskNoteDB deskNotes = new DeskNoteDB();
            if (File.Exists(fileName))
            {
                try
                {
                    deskNotes.ReadXml(fileName, XmlReadMode.ReadSchema); ////was dsDeskNotes.
                }
                catch (XmlException ex)
                {
                    Logger.WriteLine("Read DeskNotes file error: " + ex.Message);
                    //Ask before overwriting existing non desknotes file
                    string text = string.Format(Resources.AppResource.FileExists, fileName);
                    if (DialogResult.Yes == MessageBox.Show(text, Resources.AppResource.APP_TITLE,
                        MessageBoxButtons.YesNo, MessageBoxIcon.Question))
                    {
                        deskNotes.WriteXml(fileName, XmlWriteMode.WriteSchema); //was dsDeskNotes.
                    }
                    else
                        return false; //Return file not created if users says No to Overwrite
                }
            }
            else
            {
                deskNotes.WriteXml(fileName, XmlWriteMode.WriteSchema); //was dsDeskNotes.
            }
            showEmptyNote = true;
            return true;
		}

		/// <summary>
		/// Method reads all notes from data file and stores them inside this object.
		/// 
		/// </summary>
		public void ReadAllData()
		{
            DeskNoteDB.DeskNoteDataTable tblDeskNotes = this._DeskNote; //was dsDeskNotes.
			if (tblDeskNotes.Count == 0)
				return;
            // List of IDs to be removed from corrupted Desknotes dataset
            long[] idToRemove = new long[tblDeskNotes.Count];
            int counter = 0;
			foreach (DeskNoteDB.DeskNoteRow noteRow in tblDeskNotes.Rows)
			{
                Note note = new Note(noteRow.ID);
                try
                {
                    note.ReadFromDataSet(noteRow);
                    //note.DataSet = dsDeskNotes;
                    //this.Add(note);
                    notes.Add(note);
                }
                catch (System.Data.StrongTypingException ste)
                {
                    // Remember corrupted record. It cannot be removed while it is used inside foreach() cycle.
                    if (noteRow.IstextNull())
                        idToRemove[counter++] = noteRow.ID;

                    note = null;
                    Logger.WriteLine(ste.Message + "\\n" + ste.StackTrace);
                }
			}
            // Remove corrupted desknotes data set recores
            if (counter > 0)
            {
                for (int i = 0; i < counter; i++)
                {
                    RemoveDeskNote(idToRemove[i]);
                }
            }
            // Start timer to save DeskNotes in specified interval
            saveTimer = new System.Timers.Timer(SAVE_TIMER_BASIC); //10 minutes save interval
            saveTimer.Elapsed += new ElapsedEventHandler(SaveTimerElapsed);
            //saveTimer.SynchronizingObject = (System.ComponentModel.ISynchronizeInvoke)this;
            saveTimer.Enabled = true;
            this.AcceptChanges();
	    }

        /// <summary>
        /// Remove DeskNote by ID from dataset recordset including font and position
        /// It is not removed from DeskNotes main class (a list)
        /// </summary>
        /// <param name="id">ID of desknote to be removed. Used to search for it.</param>
        /// <returns>True if removal succeeded</returns>
        internal bool RemoveDeskNote(long id)
        {
            DeskNoteDB.DeskNoteDataTable tblDeskNotes = this._DeskNote; //was dsDeskNotes.
            if (tblDeskNotes.Count == 0)
                //throw exception or something.
                return false;

            // Use the Select method to find all rows matching the filter.
            DeskNoteDB.DeskNoteRow[] foundRows = (DeskNoteDB.DeskNoteRow[])tblDeskNotes.Select("ID = " + id.ToString());
            if (foundRows.Length >= 1)
            {
                // remove associated Position
                DeskNoteDB.positionRow[] positions = foundRows[0].GetpositionRows();
                if (positions.Length > 0)
                {
                    foreach (positionRow p in positions)
                    {
                        p.Delete();
                    }
                    this.position.AcceptChanges();
                }
                //remove associated extra font
                DeskNoteDB.fontRow[] fonts = foundRows[0].GetfontRows();
                if (fonts.Length > 0)
                {
                    foreach (fontRow f in fonts)
                    {
                        f.Delete();
                    }
                    this.font.AcceptChanges();
                }

                foundRows[0].Delete();

                tblDeskNotes.AcceptChanges();
                //save all data when change is done.
                Save();
                return true;
            }
            return false;
        }
        /// <summary>
        /// Cleans positions and fonts which remained in data files after removed desknotes. Due to bug #6666
        /// which removed just desknote record while positions and fonts remained in file and it was getting bigger,....
        /// </summary>
        internal void CleanOrphans()
        {
            bool saveNeeded = false;
            Properties.Settings settings = Properties.Settings.Default;
            try
            {
                 if (!settings.OrphansRemoved)
                 {
                     // remove Position records which were lost in previous versions when desknotes were removed.
                     DeskNoteDB.positionRow[] positions = (DeskNoteDB.positionRow[])this.position.Select("DeskNote_Id IS Null");
                     Logger.WriteLine("CleanOrphans will remove {0} Position records", positions.Length);
                     if (positions.Length > 0)
                     {
                         saveNeeded = true;
                         foreach (positionRow p in positions)
                         {
                             p.Delete();
                         }
                         this.position.AcceptChanges();
                     }
                     DeskNoteDB.fontRow[] fonts = (DeskNoteDB.fontRow[])this.font.Select("DeskNote_Id IS Null");
                     Logger.WriteLine("CleanOrphans will remove {0} Fonts records", fonts.Length);
                     if (fonts.Length > 0)
                     {
                         saveNeeded = true;
                         foreach (fontRow f in fonts)
                         {
                             f.Delete();
                         }
                         this.font.AcceptChanges();
                     }
                     if (saveNeeded)
                        Save();
                 }
             }
             catch (Exception e)
             {
                 Logger.WriteLine("CleanOrphans exception {0}, " + e.StackTrace, e.Message);
             }
             finally
             {
                 settings.OrphansRemoved = true;
                 settings.Save();
             }
        }

		/// <summary>
		/// Method performs showing of desknotes on a desktop.
		/// When there are no desk notes in the dataset then create a new one.
		/// </summary>
        internal void ShowDeskNotes()
		{
			if (0 != notes.Count) // was this.Count
			{
				Note topMostNote = null;
                foreach (Note note in notes)
                {	// Show all desknotes with exception of top most one. 
					// Only one desknote can be top most
					note.DataHandler = this;
					if (!note.OnTop)
					{
                        NoteForm nf = new NoteForm(note.Switchable);

						nf.NoteData = note;
                        //Set form bounds to prevent blinking of forms at different position before it is set at Load method.
                        nf.Bounds = note.Position;
						nf.Show();

						if (note.Hidden)
							nf.Hide();
					} 
					else if (note.OnTop)
					{  // remember top most desknote
						topMostNote = note;
					}
				}
				// show top most note as last one.
				if (null != topMostNote)
				{
                    NoteForm nf = new NoteForm(topMostNote.Switchable);
					nf.NoteData = topMostNote;
					nf.Show();
				}
			} 
			else if ((showEmptyNote) && (Properties.Settings.Default.ShowInitialDeskNote))
			{ // create an empty first desk note which contains just question to initiate notes adding.
				CreateNewNote(MainResource.GetString("FIRST_NOTE"), Note.DefaultPosition);
			}
			ShowingAllNotes = false;
		}
		/// <summary>
		/// Creates new DeskNote when user choose it from running application New ... actions
		/// </summary>
		public Note CreateNewNote(string text, Rectangle position)
		{
            Properties.Settings settings = Properties.Settings.Default;

			Note note = new Note(0, text, position, settings.BackgroundColor, settings.TextColor, settings.NewSwitchable);
            NoteForm nf = new NoteForm(note.Switchable);
            nf.NoteData = note;
			note.DataHandler = this;
            notes.Add(note);
			note.UpdateDataSet();
			nf.Show();
			// Top most note is reset when note is added. It needs to
			// be set again after creation.
			ConfirmTopMost();
            nf.SetResizableBorder();
            nf.txtNote.Select();
            nf.txtNote.SelectAll();
			return note;
		}

		/// <summary>
		/// Show Note on desktop from archived note.
		/// </summary>
		/// <param name="note">Note to be showed with new NoteForm created.</param>
		internal bool ShowArchivedNote(Note note)
		{
			NoteForm nf = new NoteForm(note.Switchable);
			nf.NoteData = note;
			note.DataHandler = this;
            notes.Add(note);
			note.UpdateDataSet();
			nf.Show();
			// Top most note is reset when note is added. It needs to
			// be set again after creation.
			ConfirmTopMost();
			return true;
		}

		/// <summary>
		/// Methods sets curren On top desk note again as top most. TopMost notes are reset
		/// when new DeskNote is added or hidden is made visible.
		/// </summary>
		internal void ConfirmTopMost()
		{
            foreach (Note topNote in notes)
            {
				if (topNote.OnTop)
				{
					topNote.MyForm.TopMost = true;
					break;
				}
			}
		}

		
		
		/// <summary>
		/// Method removes currently selected topmost desknote flag.
		/// This is necessary as only one form can be topmost and if 
		/// it is set to two then no one is top most.
		/// </summary>
		internal void ResetCurrentTopMost()
		{
            foreach (Note note in notes)
            {
				if (note.OnTop)
				{
					note.OnTop = false;
					note.MyForm.TopMost = false;
				}
			}
		}

        /// <summary>
        /// Save changes from dataset to xml file on disk at basic interval
        /// if no change is found in basic interval then interval is extended
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        private void SaveTimerElapsed(object source, ElapsedEventArgs e)
        {
            // If there were some unsaved changes then save them on wakeup
            if (this.HasChanges())
            {
                this.AcceptChanges();
                this.Save();
            }
            else if (saveTimer.Interval == SAVE_TIMER_BASIC)
                saveTimer.Interval = SAVE_TIMER_EXT; // 30 minutes for next check
        }
		
		/// <summary>
		/// Save main desknotes data file.
		/// </summary>
		internal void Save()
		{
            try
            {
                //if (null != dsDeskNotes)
                // Write to the file with the WriteXml method.
                this.WriteXml(Properties.Settings.Default.DataFile, XmlWriteMode.WriteSchema); //was dsDeskNotes.
            }
            catch (SecurityException e)
            {
                string text = string.Format(MainResource.GetString("ERROR_WRITE_DATA"), Properties.Settings.Default.DataFile, e.Message + "\n");
                if (DialogResult.No == MessageBox.Show(text, MainResource.GetString("APP_TITLE"),
                        MessageBoxButtons.YesNo, MessageBoxIcon.Error))
                    return;
            }
            catch (System.IO.IOException ioe)
            {   // Looks like sometimes there is IOException when Save is called from Deactivate.
                // The reason seems to be that WriteXml() from previous call is still kind of holding the file.
                // It should be at least catched. BUT! what if error will prevent some DN from saving?
                Logger.WriteLine("Save() error: " + ioe.Message);
                Logger.WriteLine("   " + ioe.TargetSite.Name + "; " + ioe.StackTrace);
                return;
            }
		}

		/// <summary>
		/// Method saves archive to disk if this was ever loaded during application lifetime.
		/// </summary>
		internal void SaveArchive()
		{
			if (null != archive)
			{
                if (archiveModified)
                {
                    // Write to the file with the WriteXml method.
                    archive.WriteXml(Properties.Settings.Default.ArchiveFile, XmlWriteMode.WriteSchema);
                    archiveModified = false;
                }
			}
		}

		/// <summary>
		/// Causes exit event to be sent and all clean up & data saving to happen.
		/// </summary>
		internal void Exit()
		{
			Application.Exit();
		}

		/// <summary>
		/// Saves archive and application settings upon exit event.
		/// </summary>
		internal void SaveAll()
		{
            try
            {
                //Archive is saved only when DeskNotes exits.
                SaveArchive();
                //run through desknotes and save them all.
                foreach(Note n in notes){
                    n.UpdateDataSet();
                }
                
                Save();
            }
            catch (SystemException e)
            {
                Logger.WriteLine("SaveArchive error: " + e.Message);
                Logger.WriteLine("   " + e.TargetSite.Name + "; " + e.StackTrace);
            }
            finally
            {
                Logger.Close();
            }
			try {
                Properties.Settings.Default.Save();
			} 
			catch (Exception e)
			{
                MessageBox.Show(Resources.AppResource.ERROR_WRITE_SETTINGS + e.Message, Resources.AppResource.APP_TITLE, 
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

        
        /// <summary>
        /// Creates disk file for archive specified by user.
        /// </summary>
        /// <returns>true if file was created on a disk, otherwise false.IOException will be thrown for some disk errors</returns>
        internal bool CreateArchiveFile(string archiveFileName)
        {
            ArchivedNotes tempArch = new ArchivedNotes();
            if (File.Exists(archiveFileName))
            {
                try
                {
                    tempArch.ReadXml(archiveFileName, XmlReadMode.ReadSchema);
                }
                catch (XmlException ex)
                {
                    Logger.WriteLine("Read Archive error: " + ex.Message);
                    //Ask before overwriting existing non archive file
                    string text = string.Format(Resources.AppResource.FileExists, archiveFileName);
                    if (DialogResult.Yes == MessageBox.Show(text, Resources.AppResource.APP_TITLE,
                        MessageBoxButtons.YesNo, MessageBoxIcon.Question))
                    {
                        tempArch.WriteXml(archiveFileName, XmlWriteMode.WriteSchema);
                        return true;
                    }
                    else
                        return false; //Return file not created if users says No to Overwrite
                }
            }
            else // Archive file does not exist.
            {
                //create new archive file with ArchivedNotes schema assigned.
                //ArchivedNotes tempArch = new ArchivedNotes();
                tempArch.WriteXml(archiveFileName, XmlWriteMode.WriteSchema);
                return true;
            }
            return true;
        }

		/// <summary>
		/// Method loads archive into memory or asks for its disk location for a very first time.
		/// When archive is loaded it's associated with ArchiveForm. ArchiveForm can be created only 
		/// once at DeskNotes. Once archive is loaded it is used forever with ArchiveForm
		/// </summary>
		/// <returns>true when archive was loaded into memory.</returns>
		internal bool LoadArchive()
		{
			try 
			{
				if (null == archive)
				{
					//return archiveForm;
                    string archiveFile = Properties.Settings.Default.ArchiveFile;
					if (!File.Exists(archiveFile))
					{
						OpenFileDialog of = new OpenFileDialog();
						of.Title = MainResource.GetString("ARCHIVE_FILE_OPEN");
                        of.FileName = archiveFile;
						of.CheckFileExists = false;
						of.DefaultExt = "xml";
						of.Filter = "XML file|*.xml|All files|*.*";
						if (DialogResult.OK == of.ShowDialog())
						{
							archiveFile = of.FileName;
                            if (CreateArchiveFile(archiveFile))
                            {
                                Properties.Settings.Default.ArchiveFile = archiveFile;
                                //create new archive file with ArchivedNotes schema assigned.
                                archive = new ArchivedNotes();
                                archive.ReadXml(archiveFile);
                                return true;
                            }
                            else
                            {
                                return false;
                            }
						} 
						else 
						{ // user escaped open file dialog without file selected. 
							// Show info to him and cancel whole action
							MessageBox.Show( MainResource.GetString("ERROR_ARCHIVE_FILE_NOT_SELECTED"),
								MainResource.GetString("APP_TITLE"),
								MessageBoxButtons.OK, MessageBoxIcon.Stop); 
							return false;
						} 
					}
					// open archive file selected by user.
					archive = new ArchivedNotes();
					archive.ReadXml(archiveFile);
					return true;
				} 
				//archive was already loaded
				return true;
			}
			catch (IOException ioe)
			{
				//display message here about no permissions or other file access problem
				string text = string.Format(MainResource.GetString("ERROR_WRONG_FILE"), Properties.Settings.Default.ArchiveFile, ioe.Message);
				MessageBox.Show(text, MainResource.GetString("APP_TITLE"),
					MessageBoxButtons.OK, MessageBoxIcon.Error);
				return false;
			} 
		}

        internal void DeleteArchiveIndex()
        {
            Properties.Settings settings = Properties.Settings.Default;
            string index = settings.SearchableArchiveUrl;
            try
            {
                File.Delete(index);
            }
            catch (Exception ex) {
                Logger.WriteLine(ex.Message + "\\n" + ex.StackTrace);
            }

        }
        /// <summary>
        /// Creates Archive Index on the background using archive filename
        /// </summary>
        internal void CreateArchiveIndex()
        {
            // Fill Index - Searchable Archive with plain text data from Archive.
            SearchableArchive index = new SearchableArchive();
            Properties.Settings settings = Properties.Settings.Default;
            index.Filename = settings.SearchableArchiveUrl;
            //index.DeleteContent();
            if (File.Exists(settings.ArchiveFile))
            {
                index.FillData(settings.ArchiveFile, index_ProgressChanged, index_RunWorkerCompleted);
            }
        }
        /// <summary>
        /// Searchable Index is being created from Archive, reports progress
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void index_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
        {
            //Logger.WriteLine("Index creation:{0}%", e.ProgressPercentage); 
        }
        /// <summary>
        /// Searchable Index was completed from Archive. It is either done or was cancelled.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void index_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            if (!e.Cancelled)
                indexReady = true;
        }

		/// <summary>
		/// ArchiveForm is disposed set ArchiveForm class holder and Archive dataset to null.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		internal void archiveForm_Disposed(object sender, EventArgs e)
		{
			archiveForm = null;
            archive = null;
		}

		public void SetNotesBackgroundColor()
		{
			if (0 != notes.Count) 
			{
                Color backColor = Properties.Settings.Default.BackgroundColor;
                int oldBackArgb = oldBackColor.ToArgb();
                foreach (Note note in notes)
                {
                    if (note.BackgroundColor.ToArgb() == oldBackArgb)
                    {
                        note.BackgroundColor = backColor;
                        note.MyForm.BackgroundColor = backColor;
                    }
                }
			}
		}

		public void SetNotesTextColor()
		{
			if (0 != notes.Count) //this.Count)
			{
                Color textColor = Properties.Settings.Default.TextColor;
                foreach(Note note in notes){
					if (note.TextColor == oldTextColor)
					{
						note.TextColor = textColor;
						note.MyForm.TextColor = textColor;
					}
				}
			}
		}
		
        

        /// <summary>
        /// Creates time stamp string that can be placed into desknote.
        /// </summary>
        internal string TimeStamp
        {
            get
            {
                System.Globalization.DateTimeFormatInfo myDTFI = System.Windows.Forms.Application.CurrentCulture.DateTimeFormat;
                System.Text.StringBuilder timeStamp = new System.Text.StringBuilder(20);
                timeStamp.Append(DateTime.Now.ToString(myDTFI.ShortDatePattern));
                if (false == Properties.Settings.Default.InsertOnlyDate)
                {
                    timeStamp.Append(" ");
                    timeStamp.Append(DateTime.Now.ToString(myDTFI.ShortTimePattern));
                }
                return timeStamp.ToString();
            }
        }

        /// <summary>
        /// Inserts timestamp into given text.
        /// </summary>
        /// <param name="text">text to be modified by timestamp</param>
        /// <param name="position">position at given text where timestamp has to be inserted</param>
        /// <returns>text with timestamp inserted</returns>
        internal string InsertTimestamp(string text, ref int position)
        {
            System.Globalization.DateTimeFormatInfo myDTFI = System.Windows.Forms.Application.CurrentCulture.DateTimeFormat;
            System.Text.StringBuilder timeStamp = new System.Text.StringBuilder(20);
            timeStamp.Append(DateTime.Now.ToString(myDTFI.ShortDatePattern));
            if (false == Properties.Settings.Default.InsertOnlyDate)
            {
                timeStamp.Append(" ");
                timeStamp.Append(DateTime.Now.ToString(myDTFI.ShortTimePattern));
            }
            int newPosition = position;
            if (0 == position)
            {   // inserts at the very beginning of text
                timeStamp.Append("\n\n");
                newPosition += timeStamp.Length - 1;
            }
            else
            {
                if ("\n" == text.Substring(position - 1, 1))
                {   // if end of line is before insertion point then don't 
                    // insert \n at the beginning of time stamp but \n\n at the end
                    timeStamp.Append("\n\n");
                    newPosition += timeStamp.Length - 1;
                }
                else
                {   // insert one \n at the beginning and one at the end.
                    timeStamp.Insert(0, "\n");
                    timeStamp.Append("\n");
                    newPosition += timeStamp.Length;
                }
            }
            string modifiedText = text.Insert(position, timeStamp.ToString());
            position = newPosition;
            return modifiedText;
        }

        internal RemindersForm RemindersWindow
        {
            get
            {
                if (null == remindersForm)
                {
                    remindersForm = new RemindersForm(this);
                    //remindersForm.Show();
                }
                return remindersForm;
            }
        }

        /// <summary>
        /// When reminders form is closed then it cannot hold reference to it.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void RemindersDisposed(object sender, EventArgs e)
        {
            remindersForm = null;
        }

        #region Archive dataset handling incl. Category and tree updates
        /// <summary>
		/// Method updates one category with new name
		/// </summary>
		/// <param name="id">ID of category to be updated</param>
		/// <param name="text">New text to be a category name</param>
		/// <param name="isExpanded">Indicates expanded state if true, otherwise collapsed</param>
		/// <returns></returns>
		internal bool UpdateCategory(long id, TreeNode parent, string text, bool isExpanded)
		{
			ArchivedNotes.CategoryDataTable categories = Archive.Category;
			ArchivedNotes.CategoryRow[] catRow = (ArchivedNotes.CategoryRow[]) categories.Select("id = " + id.ToString());
			if (catRow.Length != 0)
			{
				catRow[0].Name = text;
				if (parent != null)
					catRow[0].parentID = ((DeskNotesTreeNode)parent).ID;
				catRow[0].IsExpanded = isExpanded;
				catRow[0].AcceptChanges();
				return true;
			}
			return false;
		}

		/// <summary>
		/// Method updates just IsExpanded state of category node when it is changed
		/// in Archive tree view
		/// </summary>
		/// <param name="id">ID of category to be updated</param>
		/// <param name="isExpanded">Indicates expanded state if true, otherwise collapsed</param>
		/// <returns></returns>
		internal bool UpdateCategoryExpanded(long id, bool isExpanded)
		{
			ArchivedNotes.CategoryDataTable categories = Archive.Category;
			ArchivedNotes.CategoryRow[] catRow = (ArchivedNotes.CategoryRow[]) categories.Select("id = " + id.ToString());
			if (catRow.Length != 0)
			{
				catRow[0].IsExpanded = isExpanded;
				catRow[0].AcceptChanges();
				return true;
			}
			return false;
		}

		/// <summary>
		/// Updates ArchivedNote in dataset
		/// </summary>
		/// <param name="id"></param>
		/// <param name="catID"></param>
		/// <param name="text"></param>
		/// <returns></returns>
		internal bool UpdateArchivedNote(ArchivedNoteNode note, long catID) //string text, DateTime lastModificationTime)
		{
			ArchivedNotes.ArchivedNoteDataTable archNote = Archive.ArchivedNote;
			ArchivedNotes.ArchivedNoteRow[] noteRow = (ArchivedNotes.ArchivedNoteRow[]) archNote.Select("id = " + note.ID.ToString());
			if (noteRow.Length != 0)
			{
				noteRow[0].text = note.NoteText;
				noteRow[0].CategoryID = catID;
                noteRow[0].LastModificationTime = note.LastModificationTime;
                noteRow[0].backColor = note.PanelBackColor.ToArgb();
				noteRow[0].AcceptChanges();
				return true;
			}
			return false;
		}

		/// <summary>
		/// Removes archived note by its index including its position row.
		/// </summary>
		/// <param name="id">ID of archived note to be removed</param>
		/// <returns>true if removal was successfull otherwise false</returns>
		internal bool RemoveArchivedNote(long id)
		{
			ArchivedNotes.ArchivedNoteDataTable archNote = Archive.ArchivedNote;
			ArchivedNotes.ArchivedNoteRow[] noteRow = (ArchivedNotes.ArchivedNoteRow[]) archNote.Select("id = " + id.ToString());
			if (noteRow.Length != 0)
			{
				// Remove desktop positions if exist
				ArchivedNotes.PositionRow[] positions = noteRow[0].GetPositionRows();
				foreach (ArchivedNotes.PositionRow posRow in positions)
				{
					Archive.Position.RemovePositionRow(posRow);
					Archive.Position.AcceptChanges();
				}
				// Remove font records if exist
				ArchivedNotes.FontRow[] fonts = noteRow[0].GetFontRows();
				foreach (ArchivedNotes.FontRow fontRow in fonts)
				{
					Archive.Font.RemoveFontRow(fontRow);
					Archive.Font.AcceptChanges();
				}
				//Remove archived note itself
				Archive.ArchivedNote.RemoveArchivedNoteRow(noteRow[0]);
				Archive.ArchivedNote.AcceptChanges();
				return true;
			}
			return false;
		}

		/// <summary>
		/// Method creates tree from archive.
		/// If there is no category in the dataset then it creates the default one.
		/// </summary>
		internal CategoryNode CreateTreeFromArchive(ref TreeNode selectedNode)
		{
			CategoryNode rootCategory;
			// If there is no category inside Category table then we have to create
			// default one otherwise read all of them from table
			ArchivedNotes.CategoryDataTable categories = Archive.Category;
			if (categories.Count == 0)
			{
				// create default category node
				rootCategory = new CategoryNode(MainResource.GetString("DEFAULT_ARCHIVE_CATEGORY"));
				// new category is written into data set immediately.
				ArchivedNotes.CategoryRow row = Archive.Category.AddCategoryRow(0, rootCategory.Text, false);
				// reset parentID to null for very first default category
				row.parentID = -1;
				categories.AcceptChanges();
				SaveArchive();
			}
			else 
				// read top level category from archive dataset
			{
				ArchivedNotes.CategoryRow[] parentCat = (ArchivedNotes.CategoryRow[]) categories.Select("parentID = -1");
				rootCategory = new CategoryNode( parentCat[0].Name, parentCat[0].ID );
				if (!parentCat[0].IsIsExpandedNull())
				{
					rootCategory.IsExpanded = parentCat[0].IsExpanded;
				}
			}

			AddCategories(rootCategory, rootCategory.ID, ref selectedNode);
			return rootCategory;						
		}

		/// <summary>
		/// Method adds recursively all categories and archived nodes
		/// into given treenode.
		/// </summary>
		/// <param name="parent">Tree node which is a parent for all subcategories and Notes that could be found under it.</param>
		/// <param name="id">Category ID used to select it child categories and Notes</param>
		/// <param name="selectedNode">Node which has to be selected after last open of archive</param>
		internal void AddCategories(CategoryNode parent, long id, ref TreeNode selectedNode)
		{
			CategoryNode catNode;
			// Add sub categories of this category
			ArchivedNotes.CategoryDataTable tblCategory = Archive.Category;
			
			ArchivedNotes.CategoryRow[] categories = (ArchivedNotes.CategoryRow[]) tblCategory.Select( "parentID = " + id.ToString() );
			if (categories.Length >= 1 )
			{
				foreach (ArchivedNotes.CategoryRow catRow in categories)
				{
                    try
                    {
                        catNode = new CategoryNode(catRow.Name, catRow.ID);
                    } // Fallback when category name is not defined for some reason, but Category exists.
                    catch (System.Data.StrongTypingException ex)
                    {
                        catNode = new CategoryNode(Resources.AppResource.DEFAULT_ARCHIVE_CATEGORY, catRow.ID);
                        Logger.WriteLine(ex.Message + "\\n" + ex.StackTrace);
                    }

					if (!catRow.IsIsExpandedNull())
					{
						catNode.IsExpanded = catRow.IsExpanded;
					}
					if ((Properties.Settings.Default.SelectedNodeType == DeskNotesTreeNode.CATEGORY) && (Properties.Settings.Default.SelectedNodeId == catRow.ID))
						selectedNode = (TreeNode) catNode;

					parent.Nodes.Add(catNode);
					AddCategories( catNode, catRow.ID, ref selectedNode );
				}
			}
					
			// Add also Archived DeskNotes here
			ArchivedNotes.ArchivedNoteDataTable tblNotes = Archive.ArchivedNote;
			ArchivedNotes.ArchivedNoteRow[] noteRows = (ArchivedNotes.ArchivedNoteRow[]) tblNotes.Select( "CategoryID = " +  id.ToString());
			if (noteRows.Length >= 1 )
			{
				ArchivedNoteNode archNode;
				foreach(ArchivedNotes.ArchivedNoteRow noteRow in noteRows)
				{
					archNode = new ArchivedNoteNode(noteRow.text,noteRow.ID);
					archNode.ArchivedTime = noteRow.dateArchived;
                    if (!noteRow.IsLastModificationTimeNull())
                        archNode.LastModificationTime = noteRow.LastModificationTime;
					if (!noteRow.IsbackColorNull())
						archNode.PanelBackColor = Color.FromArgb(noteRow.backColor);
					if (!noteRow.IstextColorNull())
						archNode.PanelTextColor = Color.FromArgb(noteRow.textColor);
					archNode.Font = GetFont(noteRow);

					if ((Properties.Settings.Default.SelectedNodeType == DeskNotesTreeNode.NOTE) && (Properties.Settings.Default.SelectedNodeId == noteRow.ID))
						selectedNode = (TreeNode) archNode;
					parent.Nodes.Add( archNode );
				}
			}
		
		}

		/// <summary>
		/// Read font information from archive
		/// </summary>
		/// <param name="noteRow">ArchviedNote row to be checked for font info.</param>
		/// <returns>Font read or null if special font does not exist.</returns>
		private Font GetFont(ArchivedNotes.ArchivedNoteRow noteRow)
		{
			ArchivedNotes.FontRow[] fonts = noteRow.GetFontRows();
			if (fonts.Length > 0)
			{
				FontStyle fontStyle = FontStyle.Regular;
				bool bold = fonts[0].bold;
				bool italic = fonts[0].italic;
				bool strikeout = fonts[0].strikeout;
				bool underline = fonts[0].underline;
				fontStyle |= bold ? FontStyle.Bold : 0;
				fontStyle |= italic ? FontStyle.Italic : 0;
				fontStyle |= strikeout ? FontStyle.Strikeout : 0;
				fontStyle |= underline ? FontStyle.Underline : 0;
				Font font = new Font(fonts[0].name, fonts[0].size, fontStyle);
				return font;
			}
			return null;
		}

		/// <summary>
		/// Saves one desknote into archive.
		/// </summary>
		/// <param name="node">ArchivedNoteNode created in tree</param>
		/// <param name="note">DeskNote from desktop being archived</param>
		/// <returns></returns>
		internal bool ArchiveDeskNote(ArchivedNoteNode node, Note note)
		{
			ArchivedNotes.ArchivedNoteRow archNote = archive.ArchivedNote.AddArchivedNoteRow(((DeskNotesTreeNode)node.Parent).ID,note.Text,
				note.TextColor.ToArgb(), note.BackgroundColor.ToArgb(),node.ArchivedTime, node.LastModificationTime);
            ArchivedNotes.PositionRow position = archive.Position.AddPositionRow(archNote,note.Position.X, note.Position.Y, 
				note.Position.Width, note.Position.Height);
			if (null != note.ExtraFont)
			{
				ArchivedNotes.FontRow font = archive.Font.AddFontRow(note.ExtraFont.Name, note.ExtraFont.Size,
					note.ExtraFont.Bold, note.ExtraFont.Italic, note.ExtraFont.Strikeout, 
					note.ExtraFont.Underline, archNote);
			}
			archive.AcceptChanges();
			node.ID = archNote.ID;
			return true;
		}

		/// <summary>
		/// Adds newly created category node into archive.
		/// </summary>
		/// <param name="node">Tree view node created inside tree and being added into archive</param>
		/// <returns>true when operation completes</returns>
		internal bool AddArchiveCategory(CategoryNode node)
		{
			ArchivedNotes.CategoryRow catRow = archive.Category.AddCategoryRow(((CategoryNode)node.Parent).ID, node.Text, node.IsExpanded);
			archive.Category.AcceptChanges();
			node.ID = catRow.ID;
			return true;
		}

		/// <summary>
		/// Recusrsively removes category and all its subnodes from archive.
		/// </summary>
		/// <param name="id">Category node id to be removed</param>
		/// <returns>true when category was successfully removed.</returns>
		internal bool RemoveCategory(long id)
		{
			ArchivedNotes.CategoryRow[] categories = (ArchivedNotes.CategoryRow[]) archive.Category.Select("parentID = " + id.ToString());
			foreach(ArchivedNotes.CategoryRow catRow in categories)
			{
				this.RemoveCategory(catRow.ID);
			}
			ArchivedNotes.ArchivedNoteRow[] noteRows = (ArchivedNotes.ArchivedNoteRow[]) archive.ArchivedNote.Select("CategoryID = " + id.ToString());
			foreach(ArchivedNotes.ArchivedNoteRow note in noteRows)
			{
				ArchivedNotes.PositionRow[] position = note.GetPositionRows();
				archive.Position.RemovePositionRow(position[0]);

				ArchivedNotes.FontRow[] fonts = note.GetFontRows();
                if (fonts.Length > 0)
				    archive.Font.RemoveFontRow(fonts[0]);

				archive.ArchivedNote.RemoveArchivedNoteRow(note);
			}
			ArchivedNotes.CategoryRow[] category = (ArchivedNotes.CategoryRow[]) archive.Category.Select("ID = " + id.ToString());
			archive.Category.RemoveCategoryRow(category[0]);
			archive.AcceptChanges();
			return true;
		}

		/// <summary>
		/// Get all Note information from archive for its recreation 
		/// on desktop. Note is only copied to newly created one.
		/// </summary>
		/// <param name="id">id of Archived Note </param>
		/// <returns>Note created from archived data or null</returns>
		internal Note GetNoteFromArchive(long id)
		{
			ArchivedNotes.ArchivedNoteDataTable archNote = Archive.ArchivedNote;
			ArchivedNotes.ArchivedNoteRow[] noteRow = (ArchivedNotes.ArchivedNoteRow[]) archNote.Select("id = " + id.ToString());
			if (noteRow.Length != 0)
			{
				Color textColor = Color.FromArgb(noteRow[0].textColor);
				Color backColor = Color.FromArgb(noteRow[0].backColor);
				Rectangle position = Note.DefaultPosition;
				ArchivedNotes.PositionRow[] pos = noteRow[0].GetPositionRows();
				if (pos.Length != 0)
				{
					position = new Rectangle(pos[0].x, pos[0].y, pos[0].width, pos[0].height);
				}
				Font font = this.GetFont(noteRow[0]);
				Note note;
                Properties.Settings settings = Properties.Settings.Default;
				if (null != font)
					note = new Note(0,noteRow[0].text, position, backColor, textColor, font, settings.NewSwitchable);
				else
                    note = new Note(0, noteRow[0].text, position, backColor, textColor, null, settings.NewSwitchable);
                if (!noteRow[0].IsLastModificationTimeNull())
                {
                    note.LastModificationTime = noteRow[0].LastModificationTime;
                }
				return note;
			}
			return null;
		}

        /// <summary>
        /// Return Category Name from associated Note ID.
        /// </summary>
        /// <param name="id">Note ID for which parent category is being searched.</param>
        /// <returns>Category name or empty string.</returns>
        internal string GetCategoryNameFromNoteID(long id)
        {
            ArchivedNotes.ArchivedNoteDataTable archNote = Archive.ArchivedNote;
            ArchivedNotes.ArchivedNoteRow[] noteRow = (ArchivedNotes.ArchivedNoteRow[])archNote.Select("id = " + id.ToString());
            if (noteRow.Length > 0)
            {
                ArchivedNotes.CategoryRow[] catRow = (ArchivedNotes.CategoryRow[])Archive.Category.Select("id = " + noteRow[0].CategoryID.ToString());
                if (0 < catRow.Length)
                {
                    return catRow[0].Name;
                }
            }
            return "";
        }
        #endregion

       

        #region Print handling
        /// <summary>
		/// Delegate handling actual printing of notes.
		/// </summary>
		delegate void PrintInBackgroundDelegate();
		
		/// <summary>
		/// Prints more notes from array.
		/// </summary>
		/// <param name="notes">notes array</param>
		/// <param name="disableMe">menu to be disabled during background printing</param>
		internal void PrintNotes(IPrintableNote[] notes, params System.Windows.Forms.ToolStripMenuItem[] disableMe)
		{
			notePrintDoc = new NotePrintDocument(this.pageSettings, notes);
			System.Windows.Forms.PrintDialog printDlg = new PrintDialog();
			printDlg.AllowSelection = true;
			printDlg.AllowSomePages = true;
			printDlg.Document = notePrintDoc;
			if (DialogResult.OK == printDlg.ShowDialog())
			{
				PrintController printController = new StandardPrintController();
				notePrintDoc.PrintController = printController;
				disabledPrintMenus = disableMe;
				PrintInBackgroundDelegate d = new PrintInBackgroundDelegate(PrintInBackground);
				d.BeginInvoke(new AsyncCallback(PrintInBackgroundComplete), null);
			}

		}

		/// <summary>
		/// Prints one note from desktop or from archive on background.
		/// </summary>
		/// <param name="note">Note to be printed</param>
		/// <param name="disableMe">Print menu that has to be disabled when doing backg. printing.</param>
		internal void PrintOneNote(IPrintableNote note, params System.Windows.Forms.ToolStripMenuItem[] disableMe)
		{
			
			notePrintDoc = new NotePrintDocument(this.pageSettings, note);
			System.Windows.Forms.PrintDialog printDlg = new PrintDialog();
			printDlg.AllowSelection = true;
			printDlg.AllowSomePages = true;
			printDlg.Document = notePrintDoc;
			if (DialogResult.OK == printDlg.ShowDialog())
			{
				//PrintController printController = new StandardPrintController();
				//notePrintDoc.PrintController = printController;
				disabledPrintMenus = disableMe;
				PrintInBackgroundDelegate d = new PrintInBackgroundDelegate(PrintInBackground);
				d.BeginInvoke(new AsyncCallback(PrintInBackgroundComplete), null);
			}
		}

		private void PrintInBackground()
		{
			try
			{
				EnablePrintMenus = false;
				notePrintDoc.Print();
			}
			catch (Exception e)
			{
				EnablePrintMenus = true;
				MessageBox.Show(e.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void PrintInBackgroundComplete(IAsyncResult r)
		{
			EnablePrintMenus = true;
			notePrintDoc.Dispose();
			notePrintDoc = null;
		}

		/// <summary>
		/// Disables Print menu item during background print. 
		/// When done it enables it.
		/// </summary>
		private bool EnablePrintMenus
		{ 
			set 
			{
                if (null != disabledPrintMenus)
                {
                    //foreach (System.Windows.Forms.ToolStripMenuItem menu in disabledPrintMenus)
                      //  if (null != menu)
                            //menu.Enabled = value;
                }
			}
        }
        #endregion

        #region Accessors to List<Note> members
        internal IEnumerator GetEnumerator()
        {
            return notes.GetEnumerator();
        }
        internal int Count
        {
            get
            {
                return notes.Count;
            }
        }
        internal void Remove(Note note)
        {
            notes.Remove(note);
        }
        internal void CopyTo(Note[] noteArray)
        {
            notes.CopyTo(noteArray);
        }
        #endregion

        
    }


	public class MainResource
	{
		public static ResourceManager mainResource = new ResourceManager("DeskNotes.Resources.AppResource",System.Reflection.Assembly.GetExecutingAssembly());

		public static string GetString(string key)
		{
			return mainResource.GetString(key);
		}

	}
}
